package api;

// 向服务器发送用户当前编写的代码，并且获取到结果

import com.fasterxml.jackson.databind.ObjectMapper;
import common.CodeInvalidException;
import common.ProblemNotFoundException;
import compile.Answer;
import compile.Question;
import compile.Task;
import dao.Problem;
import dao.ProblemDao;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Queue;

@WebServlet("/compile")
public class CompileServlet extends HttpServlet {
    // 针对编译运行来说，请求和响应都是JSON格式的数据，为了方便解析和构造，就可以创建两个类来对应JSON结构。
    static class CompileRequest {
        public int id;
        public String code;
    }
    static class CompileResponse {
        // 约定error为0表示编译运行ok，1表示编译出错，2表示运行异常（用户提交了代码），3表示其他错误
        public int error;
        public String reason;
        public String stdout;
    }

    // 目的是为了使用JSON格式
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 临时加该行代码来获取smart Tomcat的工作目录
        System.out.println("用户当前工作目录：" + System.getProperty("user.dir"));

        CompileResponse compileResponse = new CompileResponse();
        CompileRequest compileRequest = null;

        try {
            // 注意设置状态、类型
            resp.setStatus(200);
            resp.setContentType("application/json;charset=utf8");

            // 1. 先读取请求的正文，按照JSON格式进行解析
            String body = readBody(req);
            compileRequest = objectMapper.readValue(body,CompileRequest.class);

            // 2. 根据id从数据库中查找到题目的详情，并得到测试用例代码
            ProblemDao problemDao = new ProblemDao();
            Problem problem = problemDao.selectOne(compileRequest.id);
            // 可能会找不到该题目，即id不存在，则加上判断
            if(problem == null) {
                // 为了统一处理错误，直接抛出一个异常
                throw new ProblemNotFoundException();
            }
            // testCode是测试用例的代码
            String testCode = problem.getTestCode();
            // requestCode 是用户提交的代码
            String requestCode = compileRequest.code;

            // 3. 把用户提交的代码和测试用例的代码进行拼接成一个完整代码
            String finalCode = mergeCode(testCode,requestCode);
            // System.out.println(finalCode); // 测试拼接结果
            // 判断用户提交的代码是否有问题
            if (finalCode == null) {
                // 用户提交代码不合法，也是抛出一个异常
                throw new CodeInvalidException();
            }

            // 4. 创建一个Task实例，调用里面的compileAndRun方法进行编译运行
            Task task = new Task();
            Question question = new Question();
            question.setCode(finalCode); // 设置代码文件
            Answer answer = task.compileAndRun(question); // 编译运行

            // 5. 根据Task运行的结果，包装成一个HTTP响应并返回
            compileResponse.error = answer.getError();
            compileResponse.reason = answer.getReason();
            compileResponse.stdout = answer.getStdout();
//            // 转为JSON格式字符串
//            String respString = objectMapper.writeValueAsString(compileResponse);
//            resp.getWriter().write(respString);
        } catch (ProblemNotFoundException e) {
            //  处理题目没有找到的异常
            compileResponse.error = 3;
            compileResponse.reason = "没有找到指定的题目： id= " + compileRequest.id;
//            // 转为JSON格式字符串
//            String respString = objectMapper.writeValueAsString(compileResponse);
//            resp.getWriter().write(respString);
        } catch (CodeInvalidException e) {
            // 处理用户提交的代码异常
            compileResponse.error = 3;
            compileResponse.reason = "提交的代码不合要求";
//            // 转为JSON格式字符串
//            String respString = objectMapper.writeValueAsString(compileResponse);
//            resp.getWriter().write(respString);
        } finally {
            // 对于重复代码，直接放到最后进行执行就行
            // 转为JSON格式字符串
            String respString = objectMapper.writeValueAsString(compileResponse);
            resp.getWriter().write(respString);
        }
    }

    // 所谓的“合并”代码：其实就是将testCode的代码嵌入到requestCode中
    // 把testCode的代码放到Solution的最后一个大括号之前就行
    private static String mergeCode(String testCode, String requestCode) {
        // 1. 查找requestCode中的最后一个}
        int index = requestCode.lastIndexOf("}");
        if(index == -1) {
            // 说明提交时完全没有}，即是不合法的代码
            return null;
        }
        // 2. 进行字符串的截取
        String subStr = requestCode.substring(0,index);
        // 进行拼接
        return subStr + testCode + "\n}";
    }

    private static String readBody(HttpServletRequest req) throws UnsupportedEncodingException {
        // 1. 先根据请求头里面的ContentLength获取到body的长度（单位是字节）
        int contentLength = req.getContentLength();
        // 2. 按照这个长度准备一个byte[]进行存储
        byte[] buffer = new byte[contentLength];
        // 3. 通过req里面的getInputStream方法获取到body的流对象
        // 要关闭资源，所以直接使用try with resource
        try (InputStream inputStream = req.getInputStream()){
            // 4. 基于这个流对象读取内容，然后把内容存放到byte[]数组中即可
            inputStream.read(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 5. 把这个byte[]数组的内容构造成一个String
        // 这里要指明对应的编码方式
        return new String(buffer,"utf8");
    }
}
